import { Inject, Injectable } from '../decorators/inject';
import { NExcel } from '../types';
import { Select } from './select';
import { DrawHelper } from '../utils/draw-helper';
import { Config } from './config';
import { TableDatas } from './table-datas';
import { Hook } from './hook';
import { Cache } from './cache';
import { FreezeRender } from './freeze-render';
import { clip } from '../utils';
@Injectable('RowHeader')
export class RowHeader {
    private ctx: CanvasRenderingContext2D;
    public startRow: number = null;
    protected selectRowIdxs: number[] = [];
    private drawLine: (start: {x: number,y: number}, end: {x: number, y: number}) => void = () => {};
    private drawActiveLine: (start: {x: number,y: number}, end: {x: number, y: number}) => void = () => {};
    constructor(
        @Inject(NExcel.CanvasToken) protected canvasEle: HTMLCanvasElement,
        protected select: Select,
        protected conf: Config,
        protected datas: TableDatas,
        protected hook: Hook,
        protected cache: Cache,
        protected freezeRender: FreezeRender
    ) {
    }
    protected init() {
        this.ctx = this.canvasEle.getContext('2d');
        this.drawLine = DrawHelper.line(this.ctx)({width: 1, type: 'solid', color: '#ccc'});
        this.drawActiveLine = DrawHelper.line(this.ctx)({width: 1, type: 'solid', color: '#0188fb'});
        this.hook.afterSelect.add((range) => {
            this.selectRowIdxs = [];
            if(range == null) {
                this.render();
                return;
            }
            for(let i = 0; i < range.rowspan; i++) {
                this.selectRowIdxs.push(i+range.row);
            }
            this.render();
        });
    }
    protected drawBg() {
        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.fillStyle = '#f0f0f0';
        this.ctx.fillRect(0,this.conf.header.col.height,this.conf.header.row.width, this.canvasEle.height);
        this.ctx.stroke();
        this.ctx.restore();
    }
    public getStartRow() {
        return this.startRow;
    }
    protected drawRightLine() {
        let opt: any = {width: 1, color: '#ccc', type: 'solid'};
        let start = {x: this.conf.header.row.width, y: 0};
        let end = {x: this.conf.header.row.width, y: this.canvasEle.height};
        DrawHelper.line(this.ctx)(opt)(start, end);
    }
    protected drawRowHeaderCell() {
        const freezeHeight = this.freezeRender.getStartRowHeight();
        let heightTotal = freezeHeight;
        let maxBreakRow = -1;
        let row = this.conf.freeze.row + 1;
        this.startRow = null;
        let lastPostion = this.conf.header.col.height + freezeHeight;
        let startRender = false;
        while(true) {
            if(maxBreakRow++ > 10000) break;
            let start = { y: heightTotal  - this.conf.view.row};
            const rowHeight = this.datas.getRowHeight(row);
            if(start.y + rowHeight <= freezeHeight) {
                heightTotal += rowHeight;
                row++;
                continue;
            }

            if(heightTotal - this.conf.view.row - this.conf.header.col.height > this.canvasEle.height) {
                console.log('row render complete!')
                break;
            }
            if(heightTotal <= this.conf.view.row + this.canvasEle.height - this.conf.header.col.height) {
                if(startRender === false) {
                    startRender = true;
                }
                if(this.startRow == null) {
                    this.startRow = row;
                }
                heightTotal += rowHeight;
                let y = heightTotal - this.conf.view.row + this.conf.header.col.height;
                let start = {x: 0, y: y};
                let end = {x: this.conf.header.row.width, y: y};
                if(this.selectRowIdxs.indexOf(row) != -1) {
                    const activeY = start.y - rowHeight;
                    const activeH = rowHeight;
                    if(row > this.conf.freeze.row) {
                        const startY = this.freezeRender.getStartRowHeight() + this.conf.header.col.height;
                        if(activeY < startY) {
                            clip(this.ctx, () => {
                                this.cache.setRowClipPosition(row, startY, activeY + activeH);
                                this.ctx.rect(0, startY, this.conf.header.row.width, activeY + activeH - startY);
                            }, () => {
                                this.drawActiveRow(activeY, activeH);
                            });
                        } else {
                            this.drawActiveRow(activeY, activeH);
                        }
                    } else {
                        this.drawActiveRow(activeY, activeH);
                    }
                }
                this.drawLine(start, end);
                this.cache.setRowPostion(row, end.y - rowHeight, end.y);
                lastPostion = end.y;
                this.drawCell(row, y - (rowHeight - 1) / 2);
                row++;
            } else {
                this.cache.setRowBoundary(this.startRow, row - 1);
                break;
            }
        }
    }

    protected drawActiveRow(y: number, h: number) {
        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.fillStyle="#dddddd";
        const drawPosition = {
            x: 0,
            y: y
        }
        const drawSize = {
            w: this.conf.header.row.width,
            h: h
        }
        this.ctx.fillRect(drawPosition.x, drawPosition.y,drawSize.w, drawSize.h);
        this.ctx.stroke();
        this.ctx.restore();
        this.drawActiveLine(
            {
                x: this.conf.header.row.width - 1, 
                y: y
            },
            {
                x: this.conf.header.row.width - 1,
                y: y + h
            }
        );
    }
    protected drawCell(row: number, y: number) {
        const freezeHeight = this.freezeRender.getStartRowHeight();
        if(row <= this.conf.freeze.row) {
            clip(this.ctx, () => {
                this.ctx.rect(0, this.conf.header.col.height, this.conf.header.row.width, freezeHeight)
            }, () => {
                this.ctx.save();
                this.ctx.beginPath();
                this.ctx.textAlign = 'center';
                this.ctx.textBaseline = 'middle';
                this.ctx.font = `${10*this.conf.enlarge}px '微软雅黑','宋体'`;
                const drawPosition = {
                    x: (this.conf.header.row.width - 1) / 2,
                    y: y
                }
                if(!this.conf.hiddenRows.has(row)) {
                    this.ctx.fillText(String(row + 1), drawPosition.x, drawPosition.y);
                }
                this.ctx.stroke();
                this.ctx.restore();
            })
        } else {
            clip(this.ctx, () => {
                let startY = this.freezeRender.getStartRowHeight()+this.conf.header.col.height;
                this.ctx.rect(0, startY, this.conf.header.row.width, this.canvasEle.clientHeight * this.conf.pixelRatio - startY)
            }, () => {
                this.ctx.save();
                this.ctx.beginPath();
                this.ctx.textAlign = 'center';
                this.ctx.textBaseline = 'middle';
                this.ctx.font = `${10*this.conf.enlarge}px '微软雅黑','宋体'`;
                const drawPosition = {
                    x: (this.conf.header.row.width - 1) / 2,
                    y: y
                }
                if(!this.conf.hiddenRows.has(row)) {
                    this.ctx.fillText(String(row + 1), drawPosition.x, drawPosition.y);
                }
                this.ctx.stroke();
                this.ctx.restore();
            })
            
        }
    }
    protected drawFreezeBg() {
        // nothing
    }
    protected drawFreezeRightLine() {
        // nothing
    }
    protected drawFreezeHeaderCell() {
        if(this.conf.freeze.row === -1) {
            return;
        }
        const freezeRow = this.conf.freeze.row + 1;
        let lastPostion = this.conf.header.col.height;
        let x = 0;
        let w = this.conf.header.row.width;
        for(let row = 0; row < freezeRow; row++) {
            let h = this.datas.getRowHeight(row);
            let y = lastPostion;
            this.drawLine({x:0, y: y + h}, {x: this.conf.header.row.width - 1, y: y + h});
            lastPostion = y + h;
            this.drawCell(row, y + h / 2);
            this.cache.setRowPostion(row, y, lastPostion);
        }
    }
    protected renderFreezeHeader() {
        this.drawFreezeBg();
        this.drawFreezeRightLine();
        this.drawFreezeHeaderCell();
    }
    protected storyRowPosition() {
        let height = this.freezeRender.getStartRowHeight();
        this.cache.updateFreezeRowPosition(height);
    }
    public render() {
        this.hook.beforeRenderRowHeader.excute();
        this.storyRowPosition();
        this.drawBg();
        this.drawRightLine();
        this.renderFreezeHeader();
        this.drawRowHeaderCell();
        this.hook.afterRenderRowHeader.excute();
    }
}